﻿#include "HelloWorldScene.h"

#include "GnsClientFrame.h"
#include <assert.h>

#include "SystemMgr.h"
#include "GnsOften.h"

#include "MyHandRes\TableMgr.h"
#include "MyHandRes\datatable.h"

using namespace cocostudio::timeline;
using namespace ui;

// 第三种方法：使用CRT库的mbstowcs()函数和wcstombs()函数，平台无关，需设定locale。
#include <string>
#include <locale.h>

USING_NS_CC;

Scene* HelloWorld::createScene()
{
    // 'scene' is an autorelease object
    auto scene = Scene::create();
    
    // 'layer' is an autorelease object
    auto layer = HelloWorld::create();

    // add layer as a child to scene
    scene->addChild(layer);

    // return the scene
    return scene;
}

HelloWorld::~HelloWorld()
{
	if (_writeToRepositoryThread.joinable())
	{
		_writeToRepositoryThread.join();
	}

	if (_readFromRepositoryThread.joinable())
	{
		_readFromRepositoryThread.join();
	}

	int iTmp = 0;
}

// on "init" you need to initialize your instance
bool HelloWorld::init()
{
    /**  you can create scene with following comment code instead of using csb file.
    // 1. super init first
    if ( !Layer::init() )
    {
        return false;
    }
    
    Size visibleSize = Director::getInstance()->getVisibleSize();
    Vec2 origin = Director::getInstance()->getVisibleOrigin();

    /////////////////////////////
    // 2. add a menu item with "X" image, which is clicked to quit the program
    //    you may modify it.

    // add a "close" icon to exit the progress. it's an autorelease object
    auto closeItem = MenuItemImage::create(
                                           "CloseNormal.png",
                                           "CloseSelected.png",
                                           CC_CALLBACK_1(HelloWorld::menuCloseCallback, this));
    
	closeItem->setPosition(Vec2(origin.x + visibleSize.width - closeItem->getContentSize().width/2 ,
                                origin.y + closeItem->getContentSize().height/2));

    // create menu, it's an autorelease object
    auto menu = Menu::create(closeItem, NULL);
    menu->setPosition(Vec2::ZERO);
    this->addChild(menu, 1);

    /////////////////////////////
    // 3. add your codes below...

    // add a label shows "Hello World"
    // create and initialize a label
    
    auto label = Label::createWithTTF("Hello World", "fonts/Marker Felt.ttf", 24);
    
    // position the label on the center of the screen
    label->setPosition(Vec2(origin.x + visibleSize.width/2,
                            origin.y + visibleSize.height - label->getContentSize().height));

    // add the label as a child to this layer
    this->addChild(label, 1);

    // add "HelloWorld" splash screen"
    auto sprite = Sprite::create("HelloWorld.png");

    // position the sprite on the center of the screen
    sprite->setPosition(Vec2(visibleSize.width/2 + origin.x, visibleSize.height/2 + origin.y));

    // add the sprite as a child to this layer
    this->addChild(sprite, 0);
    **/
    
    //////////////////////////////
	

    // 1. super init first
    if ( !Layer::init() )
    {
        return false;
    }
    
    auto rootNode = CSLoader::createNode("MainScene.csb");
	m_pRootNode = rootNode;

    addChild(rootNode);

	/////////////////////////////////////////////////////////

	// 注册同步事件监听器
	addCustomEventListener();

	//--- 按钮 -------------------------------------------
	auto pBt1 = static_cast<Button*>(rootNode->getChildByName("BT1"));
	pBt1->addTouchEventListener(CC_CALLBACK_2(HelloWorld::onBt1, this));

	auto pBt2 = static_cast<Button*>(rootNode->getChildByName("BT2"));
	pBt2->addTouchEventListener(CC_CALLBACK_2(HelloWorld::onBt2, this));

	auto pBt3 = static_cast<Button*>(rootNode->getChildByName("BT3"));
	pBt3->addTouchEventListener(CC_CALLBACK_2(HelloWorld::onBt3, this));

	auto pBt4 = static_cast<Button*>(rootNode->getChildByName("BT4"));
	pBt4->addTouchEventListener(CC_CALLBACK_2(HelloWorld::onBt4, this));

	auto pBt5 = static_cast<Button*>(rootNode->getChildByName("BT5"));
	pBt5->addTouchEventListener(CC_CALLBACK_2(HelloWorld::onBt5, this));

	auto pBt6 = static_cast<Button*>(rootNode->getChildByName("BT6"));
	pBt6->addTouchEventListener(CC_CALLBACK_2(HelloWorld::onBt6, this));

	auto pBt7 = static_cast<Button*>(rootNode->getChildByName("BT7"));
	pBt7->addTouchEventListener(CC_CALLBACK_2(HelloWorld::onBt7, this));

	auto pBt8 = static_cast<Button*>(rootNode->getChildByName("BT8"));
	pBt8->addTouchEventListener(CC_CALLBACK_2(HelloWorld::onBt8, this));

	auto pBt9 = static_cast<Button*>(rootNode->getChildByName("BT9"));
	pBt9->addTouchEventListener(CC_CALLBACK_2(HelloWorld::onBt9, this));



    return true;
}

//void HelloWorld::onExit()
//{
//	int iTmp = 0;
//}

// 进入场景后，要初始化内容
void HelloWorld::onEnterTransitionDidFinish()
{
	// 定时器
	this->schedule(schedule_selector(HelloWorld::onTimer01), 0.05f);

	// 事件桥梁定时器
	this->schedule(schedule_selector(HelloWorld::onEventBridge), 0.08f);


}

// 离开场景前，要释放内容
void HelloWorld::onExitTransitionDidStart()
{
	// 杀掉定时器
	this->unschedule(schedule_selector(HelloWorld::onTimer01));

	// 杀掉事件桥梁定时器
	this->unschedule(schedule_selector(HelloWorld::onEventBridge));
}

// 线程，向数据仓库写入数据，用于压力测试
void HelloWorld::threadWriteToRepository()
{
	GnsClientFrame* pGnsClientFrame = GnsClientFrame::getInstance();
	assert(pGnsClientFrame);
	IDataRepository*	 pDataRepository = pGnsClientFrame->_pDataRepository;
	assert(pDataRepository);

	int iCount = 0;
	while (iCount++ < THREAD_MAX_LOOP)
	{
		std::this_thread::sleep_for(std::chrono::milliseconds(THREAD_SLEEP_IN_LOOP));		// 毫秒级睡眠

		TEST_USER_INFO info;

		info.bClientPrint = true;					// 客户端打印
		info.bClientResend = true;				// 客户端收到后回发

		info.bServerPrint = true;					// 服务端打印
		info.bserverResend = true;				// 服务端收到后回发

		info.iClientResendElapse = 500;	// 客户端 回发时的睡眠间隔时间，ms
		info.iServerResendElapse = 600;	// 服务端 回发时的睡眠间隔时间，ms

		info.iResendMaxCount = 1000;		// 收到后回发的最大次数

		info.iCount = iCount++;
		sprintf(info.szName, "Zhang San");

		MSG_VALUE::STATE  iState = MSG_VALUE::DS_WRITE_FINISH;

		int iMainCode = 10;
		int iSubCode = 100;
		int iT3Code = 8;
		pDataRepository->writeToRepository(iMainCode, iSubCode, iT3Code, iState, (char*)(&info), sizeof(info));

		int iTmp = 0;
	}

	log("exit  threadWriteToRepository");
}

// 线程，从数据仓库中读取数据，用于压力测试
void HelloWorld::threadReadFromRepository()
{
	GnsClientFrame* pGnsClientFrame = GnsClientFrame::getInstance();
	assert(pGnsClientFrame);
	IDataRepository*	 pDataRepository = pGnsClientFrame->_pDataRepository;
	assert(pDataRepository);

	int iCount = 0;
	while (iCount++ < THREAD_MAX_LOOP)
	{
		std::this_thread::sleep_for(std::chrono::milliseconds(THREAD_SLEEP_IN_LOOP));		// 毫秒级睡眠

		int iMainCode = 10;
		int iSubCode = 100;
		int iT3Code = 8;

		MSG_VALUE msgValue;	// 使用完毕一定要执行 releaseVecRecord 进行释放

		// 从数据仓库中读取数据
#if 1
		pDataRepository->readFromRepository(iMainCode, iSubCode, iT3Code, msgValue);			// 试试这一句清除数据的
#else
		pDataRepository->readFromRepository(iMainCode, iSubCode, iT3Code, msgValue, false);	// 试试这一句不清数据的
#endif

		int iSize = msgValue.vecRecord.size();	// 测试用

		for (int i = 0; i < (int)msgValue.vecRecord.size(); i++)	// 遍历容器中的元素
		{
			MSG_VALUE::RECORD& e = msgValue.vecRecord[i];

			assert(sizeof(TEST_USER_INFO) == e.iDataLen);
			if (sizeof(TEST_USER_INFO) == e.iDataLen)
			{
				TEST_USER_INFO* pInfo = (TEST_USER_INFO*)e.pData;

				//log("%d  %s  state=%d ", pInfo->iCount, pInfo->szName, msgValue.state);
				auto pText01 = static_cast<Text*>(m_pRootNode->getChildByName("Text01"));

				std::string  strTmp = StringUtils::format("%d  %s  state=%d ", pInfo->iCount, pInfo->szName, msgValue.state);
				pText01->setString(strTmp);
			}
		}

		msgValue.releaseVecRecord();		// 释放 vecRecord 中的数据
	}
	
	log("exit  threadReadFromRepository");
}

// 连接结果
int HelloWorld::connectResult(int iClientId, int iSubCode, int iReconnectCount)
{
	switch (iSubCode)
	{
		case CC_START_NO_CIRCLE:				// 在 开始转圈的连接次数 以内，不转圈
		{
				log("CLIENT_INNER_CONNECT,  CC_START_NO_CIRCLE  %d", iReconnectCount);
				break;
		}
		case CC_CONNECT_CIRCLE_ING:		// 在  >开始转圈的连接次数 并且  <=结束转圈的连接次数 范围，开始转圈【正在连接】
		{
				log("CLIENT_INNER_CONNECT,  CC_CONNECT_CIRCLE_ING  %d", iReconnectCount);
				break;
		}
		case CC_SUCC_NO_CIRCLE:				// 连接成功，结束转圈
		{
				log("CLIENT_INNER_CONNECT,  CC_SUCC_NO_CIRCLE  %d", iReconnectCount);
				break;
		}
		case CC_FAIL_NO_CIRCLE:					// 结束转圈，并弹出【连接失败】的提示框
		{
				log("CLIENT_INNER_CONNECT,  CC_FAIL_NO_CIRCLE  %d", iReconnectCount);
				break;
		}
	}

	return 0;
}

// 断线重连
int HelloWorld::reConnectResult(int iClientId, int iSubCode, int iReconnectCount)
{
	switch (iSubCode)
	{
		case RECC_START_NO_CIRCLE:				// 在 开始转圈的连接次数 以内，不转圈
		{
				log("CLIENT_INNER_RE_CONNECT,  RECC_START_NO_CIRCLE  %d", iReconnectCount);
				break;
		}
		case RECC_CONNECT_CIRCLE_ING:	// 在  >开始转圈的连接次数 并且  <=结束转圈的连接次数 范围，开始转圈【正在连接】
		{
				log("CLIENT_INNER_RE_CONNECT, RECC_CONNECT_CIRCLE_ING  %d", iReconnectCount);
				break;
		}
		case RECC_SUCC_NO_CIRCLE:			// 连接成功，结束转圈
		{
				log("CLIENT_INNER_RE_CONNECT,  RECC_SUCC_NO_CIRCLE  %d", iReconnectCount);
				break;
		}
		case RECC_FAIL_NO_CIRCLE:				// 结束转圈，并弹出【连接失败】的提示框
		{
				log("CLIENT_INNER_RE_CONNECT,  RECC_FAIL_NO_CIRCLE  %d", iReconnectCount);
				break;
		}
	}

	return 0;
}


// 事件桥梁定时器的回调函数
void HelloWorld::onEventBridge(float dt)
{
	IEventBridge::EVENT_INFO eventInfo;

	while (IEventBridge::getInstance()->popFront(eventInfo))
	{
		switch (eventInfo.iMainCode)
		{
			case CLIENT_INNER_CONNECT:					// 连接结果
			{
				int& iReconnectCount = (int&)eventInfo.szBody;
				connectResult(eventInfo.iClientId, eventInfo.iSubCode, iReconnectCount);
				break;
			}
			case CLIENT_INNER_RE_CONNECT:			// 断线重连
			{
				int& iReconnectCount = (int&)eventInfo.szBody;
				reConnectResult(eventInfo.iClientId, eventInfo.iSubCode, iReconnectCount);
				break;
			}
			case CLIENT_INNER_RE_CLOSE:		// 断线
			{
				// 断线时不需要作任何处理，断线的事项交由断线重连功能（CLIENT_INNER_RE_CONNECT）进行处理
				break;
			}

		default:
			break;
		}

		int iTmp = 0;
	}
}

// 定时器的回调函数
void HelloWorld::onTimer01(float dt)
{
	GnsClientFrame* pGnsClientFrame = GnsClientFrame::getInstance();
	assert(pGnsClientFrame);
	IDataRepository*	 pDataRepository = pGnsClientFrame->_pDataRepository;
	assert(pDataRepository);

	if (1)	// 从数据仓库中读取数据
	{
		int iMainCode = 10;
		int iSubCode = 100;
		int iT3Code = 8;

		MSG_VALUE msgValue;	// 使用完毕一定要执行 releaseVecRecord 进行释放

		// 从数据仓库中读取数据
		#if 0
			pDataRepository->readFromRepository(iMainCode, iSubCode, iT3Code, msgValue, true);			// 试试这一句清除数据的
		#else
			pDataRepository->readFromRepository(iMainCode, iSubCode, iT3Code, msgValue, false);	// 试试这一句不清数据的
		#endif

		int iSize = msgValue.vecRecord.size();	// 测试用

		for (int i = 0; i < (int)msgValue.vecRecord.size(); i++)	// 遍历容器中的元素
		{
			MSG_VALUE::RECORD& e = msgValue.vecRecord[i];

			assert(sizeof(TEST_USER_INFO) == e.iDataLen);
			if (sizeof(TEST_USER_INFO) == e.iDataLen)
			{
				TEST_USER_INFO* pInfo = (TEST_USER_INFO*)e.pData;

				auto pText01 = static_cast<Text*>(m_pRootNode->getChildByName("Text01"));

				std::string  strTmp = StringUtils::format("%d  %s  state=%d ", pInfo->iCount, pInfo->szName, msgValue.state);
				pText01->setString(strTmp);

			}
		}

		msgValue.releaseVecRecord();		// 释放 vecRecord 中的数据
	}
}

void HelloWorld::onBt1(Ref * pSender, ui::Widget::TouchEventType type)
{
	//auto rootNode = CSLoader::createNode("MainScene.csb");    // 这种写法是错的，他又会建立一个 rootNode，并非 HelloWorld::init() 里面的 rootNode

	if (type == ui::Widget::TouchEventType::ENDED)
	{
		int iClientId = 0;

		log("onBt1 iClientId=%d ", iClientId);

		if (1)	// 连接服务端
		{
			bool bConnect = GnsClientFrame::getInstance()->isClientSocketValid(iClientId);

			if (bConnect)	// 连接状态
			{
				log("onBt1 closeSocket   bConnect=%d ", bConnect);
				GnsClientFrame::getInstance()->closeSocket(iClientId);		// 手工断线
			}
			else  // 断线状态
			{
				log("onBt1 manualConnect   bConnect=%d ", bConnect);
				GnsClientFrame::getInstance()->manualConnect(iClientId);	// 手动连接
			}
		}

		if (0)
		{
			auto pShowScreen = static_cast<Text*>(m_pRootNode->getChildByName("ShowScreen"));
			std::string strTmp = "this is a tree.  \n i am a man.";
			pShowScreen->setString(strTmp);

			std::string strOther = pShowScreen->getString();	// 获得原来的内容

			std::string str03 = strOther + "\n cocos2d";
			pShowScreen->setString(str03);		// 追加内容

			int iTmp = 0;
		}

		if (0)	// 这样是没问题的，因为 MY_EVENT_MSG_01 与本函数都在主线程操作
		{
			static int iCount = 1;

			TEST_USER_INFO userInof;
			sprintf (userInof.szName, "this is a tree.");
		}

		if (0)	// 启一个线程来发消息，这样发送会崩溃，说明主线程受不了其它线程投递自定义消息的冲击
		{
			std::thread	 _coreWorkerThread = std::thread(&HelloWorld::sendCustomEvent, this);;	// 发送自定义消息
			_coreWorkerThread.detach();
		}
	}
}

// 发送自定义消息
// 用线程这样发送会崩溃，说明主线程受不了其它线程投递自定义消息的冲击
// 会出现“CCEventDispatcher 1215  CCASSERT(_inDispatch == 1, "_inDispatch should be 1 here.")” 这样的断言，
// 点【忽略】不理这个崩溃就OK啦
void HelloWorld::sendCustomEvent()
{
	static int iCount = 1;
#if 0
	TEST_USER_INFO userInof;
	sprintf(userInof.szName, "this is a tree.");

	for (int i = 0; i < 2000; i++)
	{
		TRANS_TO_SCENE_MSG*  pTT = new TRANS_TO_SCENE_MSG;

		pTT->bRelease = true;
		pTT->_mainCode = 10;
		pTT->_subCode = 101;
		pTT->_dataLen = 0;

		userInof.iCount = iCount++;   // 已经在下面的循环内赋值啦

		int iLen = GnsClientFrame::min2(TRANS_TO_SCENE_MSG::DATA_MAX_LEN, sizeof(userInof));
		std::memcpy(pTT->_data, (char*)(&userInof), iLen);

		cocos2d::Director::getInstance()->getEventDispatcher()->dispatchCustomEvent(MY_EVENT_MSG_01, pTT);

		//std::this_thread::sleep_for(std::chrono::milliseconds(1));	// 睡一下就不会崩溃啦，好奇葩???
	}
#endif
}

void HelloWorld::onBt2(Ref * pSender, ui::Widget::TouchEventType type)
{
	if (type != ui::Widget::TouchEventType::ENDED)
		return;

	GnsClientFrame* pGnsClientFrame = GnsClientFrame::getInstance();
	IDataRepository*	 pDataRepository = pGnsClientFrame->_pDataRepository;
	int iRes = 0;

	if (1)	// 向数据仓库写入数据
	{
		static int iCount = 0;
		const int iFinish = 3;

		if (iCount > iFinish)		// 数量超出指定范围，不许写
			return;

		TEST_USER_INFO info;
	
		info.bClientPrint = true;					// 客户端打印
		info.bClientResend = true;				// 客户端收到后回发

		info.bServerPrint = true;					// 服务端打印
		info.bserverResend = true;				// 服务端收到后回发

		info.iClientResendElapse = 500;	// 客户端 回发时的睡眠间隔时间，ms
		info.iServerResendElapse = 600;	// 服务端 回发时的睡眠间隔时间，ms

		info.iResendMaxCount = 1000;		// 收到后回发的最大次数

		info.iCount = iCount++;
		sprintf ( info.szName, "Zhang San")  ;

		MSG_VALUE::STATE  iState = MSG_VALUE::DS_WRITEING;

		if (iCount >= iFinish)
			iState = MSG_VALUE::DS_WRITE_FINISH;

		int iMainCode = 10;
		int iSubCode = 100; 
		int iT3Code = 8;
		iRes = pDataRepository->writeToRepository(iMainCode, iSubCode, iT3Code, iState, (char*)(&info), sizeof(info));
	}
}

void HelloWorld::onBt3(Ref * pSender, ui::Widget::TouchEventType type)
{
	if (type != ui::Widget::TouchEventType::ENDED)
		return;

	GnsClientFrame* pGnsClientFrame = GnsClientFrame::getInstance();
	IDataRepository*	 pDataRepository = pGnsClientFrame->_pDataRepository;
	int iRes = 0;

	int iMainCode = 10;
	int iSubCode = 100;
	int iT3Code = 8;

	if (0)	// 测试用，OK的
	{
		// 向数据仓库写入状态，功能是OK的
		iRes = pDataRepository->setStateToRepository(iMainCode, iSubCode, iT3Code, MSG_VALUE::DS_NO);
	}

	if (0)	// 测试用，清空数据仓库中指定 Key 的 MSG_VALUE 值，OK的
	{
		//iRes = pDataRepository->clearFromRepository(iMainCode, iSubCode, iT3Code, true);			// 将容器内的 Key 和 MSG_VALUE 都移除
		iRes = pDataRepository->clearFromRepository(iMainCode, iSubCode, iT3Code, false);		// 保留Key，但清除了 MSG_VALUE 中的记录值并初始化MSG_VALUE中的几个参数
	}

	if (0) // 测试用，OK的
	{
		// 清空数据仓库中所有的数据
		iRes = pDataRepository->clearAllData();
	}

	if (1)	// 从数据仓库中读取数据
	{
		MSG_VALUE msgValue;	// 使用完毕一定要执行 releaseVecRecord 进行释放

		// 从数据仓库中读取数据
		#if 0
			iRes = pDataRepository->readFromRepository(iMainCode, iSubCode, iT3Code, msgValue);			// 试试这一句清除数据的
		#else
			iRes = pDataRepository->readFromRepository(iMainCode, iSubCode, iT3Code, msgValue, false);	// 试试这一句不清数据的
		#endif
		
		int iSize = msgValue.vecRecord.size();	// 测试用

		for (int i = 0; i < (int)msgValue.vecRecord.size(); i++)	// 遍历容器中的元素
		{
			MSG_VALUE::RECORD& e = msgValue.vecRecord[i];

			assert(sizeof(TEST_USER_INFO) == e.iDataLen);
			if (sizeof(TEST_USER_INFO) == e.iDataLen)
			{
				TEST_USER_INFO* pInfo = (TEST_USER_INFO*)e.pData;

				log("%d  %s  state=%d ", pInfo->iCount, pInfo->szName, msgValue.state);
			}
		}

		msgValue.releaseVecRecord();		// 释放 vecRecord 中的数据
	}
}

void HelloWorld::onBt4(Ref * pSender, ui::Widget::TouchEventType type)
{
	if (type != ui::Widget::TouchEventType::ENDED)
		return;

	CMemBlockPool2* pMsgPool = GnsClientFrame::getInstance()->_pMsgPool;
	assert(pMsgPool);

	GnsClientFrame* pGnsClientFrame = GnsClientFrame::getInstance();
	assert(pGnsClientFrame);

	if (1)	// 单条记录发送
	{
		int iMainCode = MAIN_TEST_USER_INFO;
		int iSubCode = SUB_TEST_USER_INFO_01;	// 显示用户信息	

		TEST_USER_INFO info;
		info.bClientPrint = true;					// 客户端打印
		info.bClientResend = true;				// 客户端收到后回发

		info.bServerPrint = true;				// 服务端打印
		info.bserverResend = true;			// 服务端收到后回发

		info.iClientResendElapse = 0;		// 客户端 回发时的睡眠间隔时间，ms
		info.iServerResendElapse = 0;	//  3000;	// 服务端 回发时的睡眠间隔时间，ms

		info.iResendMaxCount = 20000;
		info.iCount = 1;
		sprintf(info.szName, "this is a tree.");

		pGnsClientFrame->sendMsg(0, iMainCode, iSubCode, (char*)(&info), sizeof(info));
		//pGnsClientFrame->sendMsg(1, iMainCode, iSubCode, (char*)(&info), sizeof(info));
	}
}

void HelloWorld::onBt5(Ref * pSender, ui::Widget::TouchEventType type)
{
	if (type != ui::Widget::TouchEventType::ENDED)
		return;

	CMemBlockPool2* pMsgPool = GnsClientFrame::getInstance()->_pMsgPool;
	assert(pMsgPool);

	GnsClientFrame* pGnsClientFrame = GnsClientFrame::getInstance();
	assert(pGnsClientFrame);

	int iMainCode = MAIN_TEST_USER_INFO;
	int iSubCode = SUB_TEST_USER_INFO_01;	// 显示用户信息	

	TEST_USER_INFO info;
	info.bClientPrint = true;					// 客户端打印
	info.bClientResend = true;				// 客户端收到后回发

	info.bServerPrint = true;				// 服务端打印
	info.bserverResend = false;			// 服务端收到后回发

	info.iClientResendElapse = 0;		// 客户端 回发时的睡眠间隔时间，ms
	info.iServerResendElapse = 0;	//  3000;	// 服务端 回发时的睡眠间隔时间，ms

	info.iResendMaxCount = 200000;
	info.iCount = 1;
	sprintf(info.szName, "this is a tree.");

	const int iMaxLoop = 10;

	for (int i = 0; i < iMaxLoop; i++)
	{
		pGnsClientFrame->sendMsg(0, iMainCode, iSubCode, (char*)(&info), sizeof(info));

		info.iCount++;
	}
}

void HelloWorld::onBt6(Ref * pSender, ui::Widget::TouchEventType type)
{
	if (type != ui::Widget::TouchEventType::ENDED)
		return;

	// 杀掉定时器
	this->unschedule(schedule_selector(HelloWorld::onTimer01));

	// 线程，向数据仓库写入数据，用于压力测试
	_writeToRepositoryThread = std::thread(&HelloWorld::threadWriteToRepository, this);

	// 线程，从数据仓库中读取数据，用于压力测试
	_readFromRepositoryThread = std::thread(&HelloWorld::threadReadFromRepository, this);
}

std::string ws2s(const std::wstring& ws)
{
	// 需要 #include <locale.h>

	std::string curLocale = setlocale(LC_ALL, NULL); // curLocale = "C";

	setlocale(LC_ALL, "chs");

	const wchar_t* _Source = ws.c_str();
	size_t _Dsize = 2 * ws.size() + 1;
	char *_Dest = new char[_Dsize];
	memset(_Dest, 0, _Dsize);
	wcstombs(_Dest, _Source, _Dsize);
	std::string result = _Dest;
	delete[]_Dest;

	setlocale(LC_ALL, curLocale.c_str());

	return result;
}

std::wstring s2ws(const std::string& s)
{
	// 需要 #include <locale.h>

	setlocale(LC_ALL, "chs");

	const char* _Source = s.c_str();
	size_t _Dsize = s.size() + 1;
	wchar_t *_Dest = new wchar_t[_Dsize];
	wmemset(_Dest, 0, _Dsize);
	mbstowcs(_Dest, _Source, _Dsize);
	std::wstring result = _Dest;
	delete[]_Dest;

	setlocale(LC_ALL, "C");

	return result;
}

/*
本文件的格式是 UTF8，在 windos 和 android 下针对 G2U、U2G 的处理，此函数勿删
结论：
1、GB2312用2个字节表示一个中文字符，而UTF8用3个字节表示一个中文字符。
2、Android 与 IOS 是同一种标准。
3、组件输入框的手工输入内容在任何平台下都是 UTF8 字串。
4、组件在任何平台下只能显示 UTF8 字串，而 GB2312字串会是乱码。
*/
void HelloWorld::onBt7(Ref * pSender, ui::Widget::TouchEventType type)
{
	if (type == ui::Widget::TouchEventType::ENDED)
	{
		
		auto pShowScreen = static_cast<Text*>(m_pRootNode->getChildByName("ShowScreen"));

		auto pText = static_cast<TextField*>(m_pRootNode->getChildByName("TextField_01"));

		// 获得 UTF-8 的内容，中文一个字符占三个字节，英文一个字符占一个字节
		// 输入："黄河" 占 6个字节，
		// 输入“6黄7河8”占9个字节，“黄河”每个字符占3个字节即共6个字节，“678”每个字符占1个字节即共三个字节
		auto textUTF8 = pText->getString();   
		int iTextUTF8_Len = textUTF8.length();

		bool bGB_TextUTF8 = CCGnsOften::IsGB2312(textUTF8.c_str());

		if (1)  // 测试用
		{
			// 长度是9 ，“黄河”每个字符占3个字节即共6个字节，“678”每个字符占1个字节即共三个字节
			std::string strTmp = "6黄7河8";
			std::string  strUtf8_G2U = CCGnsOften::getUtf8_DirectAssignmentInProgram(strTmp);        // strG2Utf8 这个字串就是 UTF8 的
			int iLen = strUtf8_G2U.length();

			int iTmp = 0;
		}

		if (1)
		{
			//	注意：参考《G2U和U2G 内存块的细节.doc》
			// 若本文件的格式是 UTF8，在 android 环境里，这个字符串也是 UTF8 ，一个字符的长度占 3 个字节
			//	若本文件的格式是 UTF8，在 Windows 环境里，这个字符串也是 GB2312，一个字符的长度占 2 个字节
			std::string strTmp = "黄河";
			{
				std::string  strUtf8_G2U = CCGnsOften::getUtf8_DirectAssignmentInProgram(strTmp);        // strG2Utf8 这个字串就是 UTF8 的
				std::string  strGB_U2G = CCGnsOften::getGB2312_DirectAssignmentInProgram(strTmp); // strU2G_GB 这个字串就是 GB2312 的

				int iTmp = 0;
			}

			std::string  utf8String = SystemMgr::getInstance()->GbToUTF8(strTmp.c_str());
			std::string  textGB = SystemMgr::getInstance()->utf8ToGb(textUTF8.c_str());

			#if 0
					bool bGB_TextUTF8 = CCGnsOften::IsGB2312(textGB.c_str());
			#endif

			{
				//	注意：参考《G2U和U2G 内存块的细节.doc》
				// 若本文件的格式是 UTF8，在 android 环境里，这个字符串也是 UTF8 ，一个字符的长度占 3 个字节
				//	若本文件的格式是 UTF8，在 Windows 环境里，这个字符串也是 GB2312，一个字符的长度占 2 个字节
				const int iMax = 12;
				char szTmpBlock[iMax]; memset(szTmpBlock, 0, sizeof(szTmpBlock));
				memcpy(szTmpBlock, strTmp.c_str(), strTmp.length() + 1);     //  strTmp.length() 在 windows  下为 4，在 android 下为 6
				log("    strTmp length=%d", strTmp.length());
				for (int i = 0; i < iMax; i++)
					log("szTmpBlock[%d] = %x", i, szTmpBlock[i]);

				char szUtf8String[iMax]; memset(szUtf8String, 0, sizeof(szUtf8String));
				memcpy(szUtf8String, utf8String.c_str(), utf8String.length() + 1);  // utf8String.length() 为 6 ，即一个字符占三个字节 
				log("    utf8String length=%d", utf8String.length());
				for (int i = 0; i < iMax; i++)
					log("szUtf8String[%d] = %x", i, szUtf8String[i]);

				char szTextUTF8[iMax];  memset(szTextUTF8, 0, sizeof(szTextUTF8));
				memcpy(szTextUTF8, textUTF8.c_str(), textUTF8.length() + 1);   // textUTF8.length() 为 6 ，即一个字符占三个字节 
				log("    textUTF8 length=%d", textUTF8.length() );
				for (int i = 0; i < iMax; i++)
					log("szTextUTF8[%d] = %x", i, szTextUTF8[i]);

				char szTextGB[iMax];  memset(szTextGB, 0, sizeof(szTextGB));
				memcpy(szTextGB, textGB.c_str(), textGB.length() + 1);   // textGB.length() 为 4 ，
				log("    textGB length=%d",  textGB.length() );
				for (int i = 0; i < iMax; i++)
					log("szTextGB[%d] = %x", i, szTextGB[i]);

				// 显示到另一个组件的界面上
				#if CC_TARGET_PLATFORM==CC_PLATFORM_WIN32 
				{
					// 注意：先在 TextField_01 里输入“黄河”二字，才会生效
					int iType = 1;
					if (0 == iType)
						pShowScreen->setString(szTextGB);   // windows 下是乱码; 
					else if(1==iType)
						pShowScreen->setString(textUTF8);	// windows 下是正常的 
				}
				#else   // Android 与 IOS 是同一种标准
				{
					// 注意：先在 TextField_01 里输入“黄河”二字，才会生效
					int iType = 0;
					if(0==iType)
						pShowScreen->setString(szTextUTF8);	//  android 是正常的
					else if(1==iType)
						pShowScreen->setString(textUTF8);		//  android 是正常的
					else if(2==iType)
						// 若本文件的格式是 UTF8，在 android 环境里，这个字符串也是 UTF8 ，一个字符的长度占 3 个字节
						//	若本文件的格式是 UTF8，在 Windows 环境里，这个字符串也是 GB2312，一个字符的长度占 2 个字节
						pShowScreen->setString(strTmp);			//   android 是正常的
					else if (3 == iType)
						pShowScreen->setString(szTextGB);		//   android 下是乱码;
					else if (4 == iType)
						;
				}
				#endif
			}
		}
	}
}

// 林跃南的资源方式：EXCEL--》CSV--》*.h和*.cpp 
void HelloWorld::onBt8(Ref * pSender, ui::Widget::TouchEventType type)
{
	if (type == ui::Widget::TouchEventType::ENDED)
	{
		//auto pButton = (Button*)pSender;
		//int BtnTag = pButton->getTag();
		//log("this is tag ==%d", BtnTag);
		//auto pShowScreen = static_cast<Text*>(m_pRootNode->getChildByName("ShowScreen"));

		const TestDataOne* p = TableMgr::getInstance()->getDataOneTable()->getTestDataOneByID(10002);

		int iTmp = 0;

	}
}

void HelloWorld::onBt9(Ref * pSender, ui::Widget::TouchEventType type)
{
	if (type == ui::Widget::TouchEventType::ENDED)
	{
		//auto pButton = (Button*)pSender;
		//int BtnTag = pButton->getTag();
		//log("this is tag ==%d", BtnTag);

		int iTmp = 0;

		if (1)
		{
			iTmp = GnsClientFrame::getInstance()->getParamByType(0, CPT_autoReConnectTime);
			iTmp = GnsClientFrame::getInstance()->getParamByType(0, CPT_retryConnectMaxCount);
			iTmp = GnsClientFrame::getInstance()->getParamByType(0, CPT_curRetryCount);
			iTmp = GnsClientFrame::getInstance()->getParamByType(0, CPT_sendHeartbeatElapse);

			 iTmp = 101;
		}

		if (1)
		{
			char szDes[32];
			char szSrc[32];
			sprintf(szSrc, "this is a tree.");

			char szDesMem[32];
			int iCopyLen = 0;

			iCopyLen = CCGnsOften::snprintf_safe(szDes, sizeof(szDes), szSrc, strlen(szSrc));
			log("snprintf_safe  %d", iCopyLen);

			iCopyLen = CCGnsOften::memcpy_safe(szDesMem, sizeof(szDesMem), szSrc, sizeof(szSrc));
			log("memcpy_safe  %d", iCopyLen);

			int iTmp = 0;
		}

	}
}

// 注册同步事件监听器
void HelloWorld::addCustomEventListener()
{
	// 注册同步事件监听器
	cocos2d::Director::getInstance()->getEventDispatcher()->addCustomEventListener(MY_EVENT_MSG_01, CC_CALLBACK_1(HelloWorld::onMyEventCustom01, this));
	cocos2d::Director::getInstance()->getEventDispatcher()->addCustomEventListener(MY_EVENT_MSG_02, CC_CALLBACK_1(HelloWorld::onMyEventCustom02, this));

}

// 当服务端密集发送消息时，或者由线程密集发送消息时，
// 会出现“CCEventDispatcher 1215  CCASSERT(_inDispatch == 1, "_inDispatch should be 1 here.")” 这样的崩溃，
// 点【忽略】不理这个崩溃就OK啦
void HelloWorld::onMyEventCustom01(EventCustom* event)
{
	CLIENT_CORE_MSG* pCore = (CLIENT_CORE_MSG*)(event->getUserData());
	assert(sizeof(TEST_USER_INFO) == pCore->iBodyLen);
	if (sizeof(TEST_USER_INFO) != pCore->iBodyLen)
		return;

	GnsClientFrame* pGnsClientFrame = GnsClientFrame::getInstance();

	TEST_USER_INFO*  pInfo = (TEST_USER_INFO*)(pCore->pBody);
	
	if (pInfo->bClientPrint)
	{
		log("%d  %s  GnsClientFrame::OnLogicMsg  ",
			pInfo->iCount,
			pInfo->szName
			);
	}

	pInfo->iCount++;

	if (pInfo->bClientResend && pInfo->iCount <= pInfo->iResendMaxCount)
	{
		pGnsClientFrame->sendMsg(pCore->iClientId, pCore->iMainCode, pCore->iSubCode, (char*)pInfo, sizeof(TEST_USER_INFO));

		if (pInfo->iClientResendElapse > 0)
			std::this_thread::sleep_for(std::chrono::milliseconds(pInfo->iClientResendElapse));		// 毫秒级睡眠
	}
}

void HelloWorld::onMyEventCustom02(EventCustom* event)
{
	log("onMyEventCustom02");

}
